package com.ocp.socket.server.mina;

import com.ocp.socket.common.enums.MsgTypeEnum;
import com.ocp.socket.common.utils.Des3;
import com.ocp.socket.server.vo.DataVo;
import com.ocp.socket.server.vo.MessageVo;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Auther: zhangzh
 * @Date: 2019/1/23 18:27
 * @Description: 自定义解码器，确保能读到完整的包
 * @Version: 1.0.0
 */

public class ByteArrayDecoder extends CumulativeProtocolDecoder {
    private static final Logger logger = LoggerFactory.getLogger(ByteArrayDecoder.class);

    private final Charset charset;

    public ByteArrayDecoder(Charset charset) {

        this.charset = charset;

    }

    @Override
    protected boolean doDecode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput protocolDecoderOutput) throws Exception {
        //包头长度
        int packageStartLen = SocketManager.MESSAGE_START.length();
        //包尾长度
        int packageEndLen = SocketManager.MESSAGE_END.length();

        if (ioBuffer.remaining() > packageStartLen) {// 有包头，包头足够
            ioBuffer.mark();
            // 标记当前position的快照标记mark，以便后继的reset操作能恢复position位置，开始是0
            int byteLen = ioBuffer.remaining();
//            if (ioBuffer.remaining() <= byteLen) {//当字符长度大于最大时分步骤读取
//                byteLen = ioBuffer.remaining();
//            }
            //读取数据
            byte[] msg = new byte[byteLen];
            ioBuffer.get(msg);

            logger.info("获取数据：{}", new String(msg, charset));
            //开始标志
            int startIndex = new String(msg, charset).indexOf(SocketManager.MESSAGE_START);
            //第二个开始标志
            int startIndexSec = new String(msg, charset).indexOf(SocketManager.MESSAGE_START,startIndex+SocketManager.MESSAGE_START.length());
            //结束标志
            int endIndex = new String(msg, charset).indexOf(SocketManager.MESSAGE_END);

            //如果第二个开始标志 大于第一个开始标志，并且小于结束标志，则第一个开始标志数据舍弃
            if(startIndexSec > startIndex && startIndexSec< endIndex){
                startIndex = startIndexSec;
            }
            //如果从这些数据中能够获取开始标志 截取该位置并标记
            if (startIndex > -1 && endIndex > startIndex) {
                ioBuffer.reset();
                msg = new byte[startIndex];
                ioBuffer.get(msg);
                ioBuffer.mark();
                //包头舍弃
                msg = new byte[packageStartLen];
                ioBuffer.get(msg);
                //读取包体
                msg = new byte[endIndex - startIndex - packageStartLen];
                ioBuffer.get(msg);
                protocolDecoderOutput.write(this.getMessage(msg));
                //包尾舍弃
                msg = new byte[packageEndLen];
                ioBuffer.get(msg);
            } else if (startIndex > -1) {//只有开头没有结尾，舍弃开头之前的字符
                ioBuffer.reset();
                msg = new byte[startIndex];
                ioBuffer.get(msg);
                ioBuffer.mark();
                logger.info("只有开头没有结尾：{}", new String(msg, charset));
                return false;
            } else { //此时信息中无开始标志，舍弃，为了确保开始标识分开，固保留包头长度
                ioBuffer.reset();
                msg = new byte[byteLen - packageStartLen];
                ioBuffer.get(msg);
                ioBuffer.mark();
                logger.info("没有开头：{}", new String(msg, charset));
                return false;
            }
            // 读取包尾
            if (ioBuffer.remaining() > 0) {
                // 最后如果粘了包，会再次调用doDeocde()方法，把剩余数据给doDeocde()方法处理
                return true;
            }
        }
        return false; // 断包，或者执行完，
    }

    private MessageVo getMessage(byte[] msg){
        MessageVo MessageVo = new MessageVo();
        MessageVo.setReturnCode("0");
        MessageVo.setReturnMsg("操作成功");
        byte[] decMsg = null;
        String msgStr = null;
        try {
            logger.debug("接收到的信息-解码前:{}", new String(msg,charset));
            decMsg = Des3.ees3DecodeECB(SocketManager.KEY,Base64.decodeBase64(msg));
            msgStr = new String(decMsg,charset);
            logger.info("接收到的信息-解码后:{}", msgStr);
        } catch (Exception e) {
            logger.error("解码失败:" , e);
            MessageVo.setReturnCode("1");
            MessageVo.setReturnMsg(e.getMessage());
            return MessageVo;
        }
        //开始解析报文
        if(msgStr.length()>SocketManager.MESSAGE_BODY_LEN){
            int bodyLen = 0;
            try{
                bodyLen = Integer.parseInt(msgStr.substring(0,SocketManager.MESSAGE_BODY_LEN));
            }catch (NumberFormatException e){
                MessageVo.setReturnCode("1");
                MessageVo.setReturnMsg("消息长度内容不正确");
                return MessageVo;
            }

            logger.debug("字符总长度："+msgStr.length()+" 内容长度标识:"+bodyLen);
            if(msgStr.length() + SocketManager.MESSAGE_BODY_LEN>= bodyLen){
                msgStr = msgStr.substring(SocketManager.MESSAGE_BODY_LEN,bodyLen+SocketManager.MESSAGE_BODY_LEN);
                //获取终端类型
                String model = msgStr.substring(SocketManager.MODEL_INDEX,SocketManager.MODEL_LENGTH);
                MessageVo.setModel(model);
                //获取终端识别码
                String sn = msgStr.substring(SocketManager.SN_INDEX,SocketManager.SN_INDEX+SocketManager.SN_LENGTH);
                MessageVo.setSn(sn);
                //如果类型为NORM 表示为时间请求报文
                if(MsgTypeEnum.NORM.getTypeCode().equals(model)){
                    return MessageVo;
                }

                //获取数据时间
                String dataTime = msgStr.substring(SocketManager.DATA_TIME_INDEX,SocketManager.DATA_TIME_INDEX+SocketManager.DATA_TIME_LENGTH);
                MessageVo.setDataTime(dataTime);
                String returnDateHeader= msgStr.substring(0,SocketManager.DATA_INDEX);
                MessageVo.setDataHeader(returnDateHeader);

                //获取数据
                String data = msgStr.substring(SocketManager.DATA_INDEX);
                if("TEMP".equals(model)){//温度
                    this.getTempMsg(MessageVo,data);
                }else if("AIRP".equals(model)){//气压
                    this.getAIRPMsg(MessageVo,data);
                }else if("MILK".equals(model)){//挤奶清洗
                    this.getMILKMsg(MessageVo,data);
                }else if("0001".equals(model)){//登录
                    //登录只记录用户信息 不做任何处理
                }else if("0002".equals(model)){//报警处理
                    this.getAppMsg(MessageVo,data);
                }else if("0000".equals(model)){//app心跳（服务不处理）
                    //this.getMILKMsg(MessageVo,data);
                }else if("0003".equals(model)){//温度
                    //this.getMILKMsg(MessageVo,data);
                }else if("0004".equals(model)){//气压
                    //this.getMILKMsg(MessageVo,data);
                }else if("0005".equals(model)){//辅助设备(设备信号)
                    //this.getMILKMsg(MessageVo,data);
                }else if("0006".equals(model)){//报警信息
                    //this.getMILKMsg(MessageVo,data);
                }else if("0007".equals(model)){//奶厅的挤奶清洗状态
                    //this.getMILKMsg(MessageVo,data);
                }else{
                    MessageVo.setReturnCode("1");
                    MessageVo.setReturnMsg("暂不支持该数据格式");
                    return MessageVo;
                }

            }else{
                MessageVo.setReturnCode("1");
                MessageVo.setReturnMsg("内容长度太少");
                return MessageVo;
            }
        }else{
            MessageVo.setReturnCode("1");
            MessageVo.setReturnMsg("消息没有足够的长度标识");
            return MessageVo;
        }
        return MessageVo;
    }

    /**
     * 解析APP信号
     * @param messageVo
     * @param data
     */
    private void getAppMsg(MessageVo messageVo,String data){
        messageVo.setJsonStr(data);
    }

    /**
     * 解析温度信号
     * @param messageVo
     * @param data
     */
    private void getTempMsg(MessageVo messageVo,String data){
        //获取线路编码
        String[] dataArry = data.split(SocketManager.DATA_SEPARATOR);
        Map<String,DataVo> dataVoMap = new HashMap<String,DataVo>();
        DataVo dataVo = null;
        for(String lineData : dataArry){
            dataVo = new DataVo();
            //线路
            String lineCode = lineData.substring(0,SocketManager.DATA_LINE_LENGTH);
            dataVo.setLineCode(lineCode);
            //数值
            dataVo.setVal(lineData.substring(SocketManager.DATA_LINE_LENGTH));
            dataVoMap.put(lineCode,dataVo);
        }
        if(dataVoMap.size() > 0 ){
            messageVo.setDataVoMap(dataVoMap);
        }
    }

    /**
     * 解析气压信号
     * @param messageVo
     * @param data
     */
    private void getAIRPMsg(MessageVo messageVo,String data){
        //获取线路编码
        Map<String,DataVo> dataVoMap = new HashMap<String,DataVo>();
        DataVo dataVo = null;
        dataVo = new DataVo();
        //线路
        String lineCode = data.substring(0,SocketManager.DATA_LINE_LENGTH);
        dataVo.setLineCode(lineCode);
        //数值
        dataVo.setVal(data.substring(SocketManager.DATA_LINE_LENGTH,data.length()-SocketManager.DATA_VAL_UNIT_LENGTH));
        //单位
        dataVo.setUnit(data.substring(data.length() - SocketManager.DATA_VAL_UNIT_LENGTH));
        dataVoMap.put(lineCode,dataVo);

        if(dataVoMap.size() > 0 ){
            messageVo.setDataVoMap(dataVoMap);
        }
    }

    /***
     * 解析挤奶清洗信号
     * @param messageVo
     * @param data
     */
    private void getMILKMsg(MessageVo messageVo,String data){
        //获取线路编码
        Map<String,DataVo> dataVoMap = new HashMap<String,DataVo>();
        DataVo dataVo = null;
        String lineCode = null;
        String val = null;
        dataVo = new DataVo();
        //挤奶信号获取
        //线路
        lineCode = data.substring(0,SocketManager.DATA_MILK_LINE_LENGTH);
        val = data.substring(SocketManager.DATA_MILK_LINE_LENGTH,SocketManager.DATA_MILK_LINE_LENGTH+SocketManager.DATA_MILK_LINE_VAL_LENGTH);
        //线路
        dataVo.setLineCode(lineCode);
        //数值
        dataVo.setVal(val);
        dataVoMap.put(lineCode,dataVo);
        //清洗信号获取
        dataVo = new DataVo();
        //线路
        lineCode = data.substring(SocketManager.DATA_MILK_LINE_LENGTH+SocketManager.DATA_MILK_LINE_VAL_LENGTH,SocketManager.DATA_MILK_LINE_LENGTH+SocketManager.DATA_MILK_LINE_VAL_LENGTH+SocketManager.DATA_CLEAN_LINE_LENGTH);
        val = data.substring(data.length() - SocketManager.DATA_CLEAN_LINE_VAL_LENGTH);
        //线路
        dataVo.setLineCode(lineCode);
        //数值
        dataVo.setVal(val);
        dataVoMap.put(lineCode,dataVo);

        if(dataVoMap.size() > 0 ){
            messageVo.setDataVoMap(dataVoMap);
        }
    }

}
